package compile

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"path"
	"strings"

	"gitee.com/caivega/easm/core"
	"gitee.com/caivega/easm/util"
)

// replace all string with "${0....n}"
func replaceString(current *Current, content string) string {
	sb := &strings.Builder{}
	lastIndex := -1
	runes := []rune(content)
	for i := 0; i < len(runes); i++ {
		isString := (runes[i] == '"')
		if isString || lastIndex != -1 {
			if isString && lastIndex != -1 {
				value := string(runes[lastIndex+1 : i])
				key := current.pool.putString(value)
				sb.WriteString(key)
				lastIndex = -1
			} else if isString {
				lastIndex = i
			}
		} else {
			_, err := sb.WriteString(string(runes[i]))
			if err != nil {
				panic(err)
			}
		}
	}
	return sb.String()
}

// recover all string from "${0...n}"
func recoverString(current *Current, list []string) []string {
	for i := 0; i < len(list); i++ {
		item := list[i]
		value, ok := current.pool.getString(item)
		if ok {
			list[i] = value
		}
	}
	return list
}

func createParameterData(cf *Label, dt core.DataType) *Data {
	d := &Data{t: dt, attrs: make(map[string]string)}
	if cf == nil || cf.f == nil {
		return d
	}
	d.localIndex = cf.f.localCount
	cf.f.localCount++
	return d
}

func createReturnData(dt core.DataType) *Data {
	return &Data{t: dt, attrs: make(map[string]string)}
}

func createCallData(dt core.DataType, callIndex int) *Data {
	d := &Data{t: dt, attrs: make(map[string]string)}
	d.callDataIndex = callIndex
	return d
}

func createBlockItem(name string) *BlockItem {
	return &BlockItem{name: name}
}

func processBracket(content string) (int, []string) {
	if !strings.Contains(content, "(") {
		return 0, []string{content}
	}
	content = strings.Join(strings.Fields(content), "")
	leftCount := strings.Count(content, "(")
	rightCount := strings.Count(content, ")")
	if leftCount != rightCount {
		panic("error function")
	}
	sl := make([]string, 0)
	spaceIndex := -1
	leftIndex := -1
	leftCount = 0
	rightIndex := -1
	bracketCount := 0
	runes := []rune(content)
	for i := 0; i < len(runes); i++ {
		isLeft := (runes[i] == '(')
		isRight := (runes[i] == ')')
		if isLeft {
			if leftCount == 0 {
				leftIndex = i
			}
			leftCount++
		} else if isRight {
			leftCount--
			if leftCount == 0 {
				sl = append(sl, string(runes[leftIndex:i+1]))
				leftIndex = -1
				rightIndex = i + 1
				bracketCount++
			}
		} else {
			if leftCount == 0 && spaceIndex == -1 {
				spaceIndex = i
			}
		}
		if i != 0 && spaceIndex != -1 && leftIndex != -1 {
			sl = append(sl, string(runes[spaceIndex:leftIndex]))
			spaceIndex = -1
		}
	}
	if rightIndex != -1 && rightIndex != len(runes) {
		sl = append(sl, string(runes[rightIndex:]))
	}
	return bracketCount, sl
}

func processData(current *Current, n string, t core.LabelType, dataTypeString string, dataContent string) *Label {
	cf := currentFunc(current)
	label := &Label{n: n, t: t, attrs: make(map[string]string)}
	if len(dataContent) == 0 {
		dataType := core.DataValue(dataTypeString)
		label.d = createParameterData(cf, dataType)
	} else {
		dataType := core.DataValue(dataTypeString)
		label.d = createParameterData(cf, dataType)

		list := make([]string, 0)
		list0 := util.Split(dataContent, ",")
		for j := 0; j < len(list0); j++ {
			list1 := util.Split(list0[j], ":")
			name := ""
			value := ""
			switch len(list1) {
			case 1:
				name = "value"
				value = strings.TrimSpace(list1[0])
			case 2:
				name = strings.TrimSpace(list1[0])
				value = strings.TrimSpace(list1[1])
			default:
				panic("error data")
			}
			list = append(list, name, value)
		}
		list = recoverString(current, list)
		ll := len(list)
		for i := 0; i < ll; i += 2 {
			label.d.attrs[list[i]] = list[i+1]
		}
	}
	switch label.t {
	case core.LABEL_SLOT:
		section := currentSection(current)
		if section == nil || section.n != SECTION_DATA {
			panic("slot should be in section data")
		}
		label.d.slotIndex = section.s.slotCount
		section.s.slotCount++
	case core.LABEL_VAR:
		label.d.varIndex = current.memory.offset
		current.memory.offset++
	}
	return label
}

func processFunction(current *Current, s string) *Label {
	bc, segments := processBracket(s)
	label := &Label{attrs: make(map[string]string), f: &Function{}}
	if bc == 0 || bc > 2 {
		panic("error function")
	}
	label.n = segments[0]
	label.t = core.LABEL_FUNCTION

	dataContent := segments[1][1 : len(segments[1])-1]
	list0 := util.Split(dataContent, ",")
	for j := 0; j < len(list0); j++ {
		list1 := util.Split(list0[j], ":")
		if len(list1) != 2 {
			panic("error parameter")
		}
		n := strings.TrimSpace(list1[0])
		t := core.DataValue(strings.TrimSpace(list1[1]))
		label.f.params = append(label.f.params, &Label{n: n, t: core.LABEL_PARAMETER, d: createParameterData(label, t)})
	}
	multiReturn := (bc == 2)
	singleReturn := (bc == 1 && len(segments) == 3)
	if singleReturn || multiReturn {
		if singleReturn {
			dt := core.DataValue(strings.TrimSpace(segments[2]))
			label.f.returns = append(label.f.returns, &Label{t: core.LABEL_PARAMETER, d: createReturnData(dt)})
		} else { // multiReturn
			dataContent := segments[2][1 : len(segments[2])-1]
			list := util.Split(dataContent, ",")
			for j := 0; j < len(list); j++ {
				dt := core.DataValue(strings.TrimSpace(list[0]))
				label.f.returns = append(label.f.returns, &Label{t: core.LABEL_PARAMETER, d: createReturnData(dt)})
			}
		}
	}
	return label
}

func processPush(current *Current, l *Label) {
	b := &Block{nameMap: make(map[string]*Label)}

	l.b = b
	b.label = l

	current.Top().Put(b.label)
	current.Push(b)

	if l.IsLabelType(core.LABEL_FUNCTION) && l.f != nil { // function
		for i := 0; i < len(l.f.params); i++ {
			pl := l.f.params[i]
			current.Top().Put(pl)
		}
	}
}

func processPop(current *Current, t core.LabelType) {
	count := 0
	has := false

	pops := make([]*Block, 0)
	for i := len(current.blocks) - 1; i >= 0; i-- {
		b := current.blocks[i]

		count++
		pops = append(pops, b)

		if b.label != nil && b.IsType(t) {
			has = true
			break
		}
	}
	if has {
		current.blocks = current.blocks[0 : len(current.blocks)-count]

		for i := 0; i < len(pops); i++ { // exit check for function
			fb := pops[i]
			if fb.IsType(core.LABEL_FUNCTION) {
				if len(fb.label.f.returns) > 0 && !fb.label.f.hasReturn {
					panic("no return for function with returns")
				}
				if len(fb.label.f.returns) == 0 && fb.label.f.hasReturn && fb.label.n != ASM_MAIN {
					panic("has return for function without returns") // main can have return(.return)
				}
			}
		}
	}
}

func processBlockPop(current *Current, itemName string, alsoItem bool) bool {
	count := 0
	has := false
	for i := len(current.blocks) - 1; i >= 0; i-- {
		b := current.blocks[i]
		if !b.IsType(core.LABEL_BLOCK) {
			panic("should be block")
		}
		if b.label.i != nil && b.label.i.name == itemName {
			if alsoItem {
				count++
			}
			has = true
			break
		} else {
			count++
		}
	}
	if has {
		current.blocks = current.blocks[0 : len(current.blocks)-count]
	}
	return has
}

func isExpression(content string) bool {
	operatorMap := core.OperatorMap()
	contents := util.Split(content, ";")
	lli := len(contents)
	if lli == 0 {
		return false
	}
	for i := 0; i < lli; i++ {
		itemContent := contents[i]
		list := util.SplitItem(itemContent)
		llj := len(list)
		for j := 0; j < llj; j++ {
			t, ok := operatorMap[list[j]]
			if ok {
				leftCount := ((int(t) % 10000) - (int(t) % 1000)) / 1000
				rightCount := ((int(t) % 1000) - (int(t) % 100)) / 100
				if (j-leftCount) >= 0 && (j+rightCount) < llj {
					return true
				}
			}
		}
	}
	return false
}

func trimBracket(s string) (string, bool) {
	ll := len(s)
	if ll < 2 {
		return s, false
	}
	if strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
		return s[1 : ll-1], true
	}
	return s, false
}

func processExpressionItem(current *Current, content string) []*OperatorItem {
	operatorMap := core.OperatorMap()

	_, fc := processBracket(content)
	list := make([][]string, 0)
	for i := 0; i < len(fc); i++ {
		item := fc[i]
		itemContent, ok := trimBracket(item)
		if ok {
			list = append(list, []string{itemContent, "b"})
		} else {
			items := util.SplitItem(item)
			for j := 0; j < len(items); j++ {
				s := items[j]
				_, ok := operatorMap[s]
				if ok {
					list = append(list, []string{s, "o"})
				} else {
					list = append(list, []string{s, "v"})
				}
			}
		}
	}

	returns := make([]*OperatorItem, 0)
	for {
		// find the first most weighted operator
		mi := -1
		mt := -1
		mw := -1
		for i := 0; i < len(list); i++ {
			item := list[i]
			if item[1] == "o" {
				t := int(operatorMap[item[0]])
				weight := t % 100
				if weight > mw {
					mw = weight
					mi = i
					mt = t
				}
			}
		}
		if mi != -1 {
			leftCount := ((mt % 10000) - (mt % 1000)) / 1000
			rightCount := ((mt % 1000) - (mt % 100)) / 100

			oi := &OperatorItem{t: core.OperatorType(mt)}
			leftIndex := -1
			oic := &strings.Builder{}
			if leftCount > 0 {
				if leftCount != 1 {
					panic("error left count")
				}
				leftIndex = mi - leftCount
				if leftIndex < 0 {
					panic("error expression")
				}
				leftItem := list[leftIndex]
				switch leftItem[1] {
				case "s":
					leftContent := leftItem[0]
					oic.WriteString(leftContent)
					leftLabel := &Label{n: leftContent, t: core.LABEL_STACK, attrs: make(map[string]string)}
					oi.params = append(oi.params, &OperatorItem{value: leftLabel})
				case "b":
					leftContent := leftItem[0]
					oic.WriteString("(" + leftContent + ")")
					items := processExpressionItem(current, leftItem[0])
					returns = append(returns, items...)

					leftLabel := &Label{n: leftContent, t: core.LABEL_STACK, attrs: make(map[string]string)}
					oi.params = append(oi.params, &OperatorItem{value: leftLabel})
				case "v":
					leftContent := leftItem[0]
					oic.WriteString(leftContent)
					leftLabel := processValue(current, leftContent, true)
					oi.params = append(oi.params, &OperatorItem{value: leftLabel})
				default:
					panic("error expression: " + content)
				}
			} else {
				leftIndex = mi
			}
			oic.WriteString(core.OperatorType(mt).String())
			rightIndex := -1
			if rightCount > 0 {
				if rightCount != 1 {
					panic("error right count")
				}
				rightIndex = mi + rightCount
				if rightIndex >= len(list) {
					panic("error expression")
				}
				rightItem := list[rightIndex]
				switch rightItem[1] {
				case "s":
					rightContent := rightItem[0]
					oic.WriteString(rightContent)
					rightLabel := &Label{n: rightContent, t: core.LABEL_STACK, attrs: make(map[string]string)}
					oi.params = append(oi.params, &OperatorItem{value: rightLabel})
				case "b":
					rightContent := rightItem[0]
					oic.WriteString("(" + rightContent + ")")
					items := processExpressionItem(current, rightItem[0])
					returns = append(returns, items...)

					rightLabel := &Label{n: rightContent, t: core.LABEL_STACK, attrs: make(map[string]string)}
					oi.params = append(oi.params, &OperatorItem{value: rightLabel})
				case "v":
					rightContent := rightItem[0]
					oic.WriteString(rightContent)
					rightLabel := processValue(current, rightContent, true)
					oi.params = append(oi.params, &OperatorItem{value: rightLabel})
				default:
					panic("error expression: " + content)
				}
			} else {
				rightIndex = mi
			}
			leftList := list[:leftIndex]
			rightList := list[rightIndex+1:]
			list = append(leftList, []string{"(" + oic.String() + ")", "s"})
			list = append(list, rightList...)

			returns = append(returns, oi)
		} else {
			break
		}
	}
	return returns
}

func processExpression(current *Current, content string) []*OperatorItem {
	bc, fc := processBracket(content)
	if bc == 1 && len(fc) == 1 {
		content, _ = trimBracket(content)
	}
	contents := util.Split(content, ";")
	ll := len(contents)
	if ll == 0 {
		panic("empty block item")
	}
	items := make([]*OperatorItem, 0)
	for i := 0; i < ll; i++ {
		itemContent := contents[i]
		list := processExpressionItem(current, itemContent)
		items = append(items, list...)
	}
	return items
}

func createBlockItemWith(current *Current, t core.BlockItemType, name string, content string) *BlockItem {
	blockItem := createBlockItem(name)
	blockItem.t = t
	blockItem.value = content
	return blockItem
}

func processLine(current *Current, line *Line) {
	code := replaceString(current, line.code)
	fields := strings.Fields(code)
	if current.IsDebug() {
		fmt.Println("code", line.Y, line.code)
	}
	action := fields[0]
	switch {
	case action == core.LABEL_SECTION_NAME:
		sectionName := fields[1]
		_, ok := findVisible(current, sectionName)
		if ok {
			panic("exists " + core.LABEL_SECTION.String() + ": " + sectionName)
		}
		processPop(current, core.LABEL_SECTION)
		sectionLabel := &Label{n: sectionName, t: core.LABEL_SECTION, attrs: make(map[string]string)}
		processPush(current, sectionLabel)
		if current.IsDebug() {
			fmt.Println("> section", sectionName)
		}
		switch sectionName {
		case SECTION_DATA:
			sectionLabel.s = &Section{name: sectionName, slotCount: 0}
		case SECTION_CODE: // entry code
			mf := &Function{}
			sectionLabel.s = &Section{name: sectionName, main: mf}
			mainLabel := &Label{is: true, n: ASM_MAIN, t: core.LABEL_FUNCTION, attrs: make(map[string]string), f: mf}
			processPush(current, mainLabel)
		}

	case strings.HasSuffix(code, ":"): // function or label
		s := code[:len(code)-1]
		leftIndex := strings.Index(s, "(")
		if leftIndex == -1 {
			module := currentModule(current)
			section := currentSection(current)
			l, ok := module.m.labelMap[s]
			if !ok {
				panic("unknown label")
			}
			l.s = &Section{name: section.n}
			l.f = &Function{}

			if section.n == SECTION_CALL {
				processPush(current, l)
			} else {
				current.Top().Put(l)
			}

			if current.IsDebug() {
				fmt.Println("> label", l.n)
			}
		} else {
			_, ok := findVisible(current, s)
			if ok {
				panic("exists " + core.LABEL_FUNCTION.String() + ": " + s)
			}
			section := currentSection(current)
			if section.n == SECTION_CALL {
				processPop(current, core.LABEL_LABEL)
			}
			processPop(current, core.LABEL_FUNCTION)
			f := processFunction(current, s)
			processPush(current, f)
			if current.IsDebug() {
				fmt.Println("> function", f.n)
			}
		}

	default:
		if current.Top().IsData() {
			current.Pop()
		}
		switch { // switch again as the data block
		case core.IsData(action):
			dataLabel := &Label{is: true, n: getLineName(action, line.Y), t: core.LabelValue(action), attrs: make(map[string]string)}
			processPush(current, dataLabel)
			if current.IsDebug() {
				fmt.Println("> data", dataLabel.n, dataLabel.t)
			}

			dataLine := strings.Join(fields[1:], " ")
			dataSegments := util.Split(dataLine, ",")
			for i := 0; i < len(dataSegments); i++ {
				bc, fc := processBracket(dataSegments[i])

				dataItems := util.Split(fc[0], ":")
				dataName := dataItems[0]
				_, ok := findData(current, dataName)
				if ok {
					panic("exists data: " + dataName)
				}
				dataType := dataItems[1]
				dataContent := ""
				if bc > 0 {
					dataContent = fc[1][1 : len(fc[1])-1]
				}
				varLabel := processData(current, dataName, dataLabel.t, dataType, dataContent)
				current.Top().Put(varLabel)
				if current.IsDebug() {
					fmt.Println("> data", varLabel.n, varLabel.d.t)
				}
			}

		case strings.HasSuffix(code, ")"): // if, for, function call or with expression
			bc, fc := processBracket(code)
			if bc < 1 || len(fc) < 2 {
				panic("error: " + code)
			}
			callName := fc[0]
			callContent := fc[1][1 : len(fc[1])-1]
			switch callName {
			case core.BLOCK_IF:
				ifl := &Label{is: true, n: getLineName(callName, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItemWith(current, core.BLOCK_ITEM_IF, callName, callContent)}
				processPush(current, ifl)
				ifb := &Label{is: true, n: getLineName(callName+"_"+core.BLOCK_BODY, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItem(core.BLOCK_BODY)}
				processPush(current, ifb)
				if current.IsDebug() {
					fmt.Println("> if", ifl.n)
				}
			case core.BLOCK_ELSEIF:
				ok := processBlockPop(current, core.BLOCK_IF, false)
				if !ok {
					panic("no if")
				}
				elseifl := &Label{is: true, n: getLineName(callName, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItemWith(current, core.BLOCK_ITEM_IF, callName, callContent)}
				processPush(current, elseifl)
				if current.IsDebug() {
					fmt.Println("> else if", elseifl.n)
				}
			case core.BLOCK_FOR:
				forl := &Label{is: true, n: getLineName(callName, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItemWith(current, core.BLOCK_ITEM_FOR, callName, callContent)}
				processPush(current, forl)
				forb := &Label{is: true, n: getLineName(callName+"_"+core.BLOCK_BODY, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItem(core.BLOCK_BODY)}
				processPush(current, forb)
				if current.IsDebug() {
					fmt.Println("> for", forl.n)
				}
			case core.LABEL_PUSH_NAME:
				dataLine := strings.Join(fields[1:], " ")
				processPushLabel(current, action, dataLine, line)

			default:
				call := processCall(current, callName, callContent)
				l := &Label{is: true, n: getLineName(callName, line.Y), t: core.LABEL_CALL, attrs: make(map[string]string), c: call}
				current.Top().Put(l)
				if current.IsDebug() {
					fmt.Println("> call", l.n, bc)
				}
			}

		case action == core.LABEL_IFNOT_NAME:
			dataLine := strings.Join(fields[1:], " ")
			list := util.Split(dataLine, ",")
			for i := 0; i < len(list); i++ {
				list[i] = strings.TrimSpace(list[i])
			}
			if len(list) != 2 {
				panic("error ifnot")
			}
			conditionContent := list[0]
			notLabel := processValue(current, list[1], false)
			l := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_IFNOT, attrs: make(map[string]string), i: createBlockItemWith(current, core.BLOCK_ITEM_IF, action, conditionContent), p: &Parameter{name: action, params: []*Label{notLabel}}}
			current.Top().Put(l)
			if current.IsDebug() {
				fmt.Println("> ifnot", l.n)
			}

		default: // as same format: name param(0), param(1),...,param(n)
			dataLine := strings.Join(fields[1:], " ")
			switch {
			case action == core.BLOCK_ELSE:
				ok := processBlockPop(current, core.BLOCK_IF, false)
				if !ok {
					panic("no if")
				}
				elsel := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItem(action)}
				processPush(current, elsel)
				if current.IsDebug() {
					fmt.Println("> else", elsel.n)
				}

			case action == core.BLOCK_ENDIF:
				ok := processBlockPop(current, core.BLOCK_IF, false)
				if !ok {
					panic("no if")
				}
				endifl := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItem(action)}
				processPush(current, endifl)
				if current.IsDebug() {
					fmt.Println("> endif", endifl.n)
				}
				ok = processBlockPop(current, core.BLOCK_IF, true)
				if !ok {
					panic("no if")
				}

			case action == core.BLOCK_ENDFOR:
				ok := processBlockPop(current, core.BLOCK_FOR, false)
				if !ok {
					panic("no for")
				}
				endforl := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_BLOCK, attrs: make(map[string]string), i: createBlockItem(action)}
				processPush(current, endforl)
				if current.IsDebug() {
					fmt.Println("> endfor", endforl.n)
				}
				ok = processBlockPop(current, core.BLOCK_FOR, true)
				if !ok {
					panic("no for")
				}

			case action == core.LABEL_PRINT_NAME:
				params := processParameter(current, action, dataLine, false)
				l := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_PRINT, attrs: make(map[string]string), p: params}
				current.Top().Put(l)
				if current.IsDebug() {
					fmt.Println("> print", l.n)
				}

			case action == core.LABEL_STORE_NAME:
				params := processParameter(current, action, dataLine, false)
				l := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_STORE, attrs: make(map[string]string), p: params}
				current.Top().Put(l)
				if current.IsDebug() {
					fmt.Println("> store", l.n)
				}

			case action == core.LABEL_RETURN_NAME:
				params := processParameter(current, action, dataLine, false)
				cf := currentFunc(current)
				if cf != nil && cf.f != nil {
					cf.f.hasReturn = true
				}

				l := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_RETURN, attrs: make(map[string]string), p: params}
				current.Top().Put(l)
				if current.IsDebug() {
					fmt.Println("> return", l.n)
				}

			case action == core.LABEL_PUSH_NAME:
				processPushLabel(current, action, dataLine, line)

			default: // as the instruction may mix with the key word
				ip := ""
				if strings.HasPrefix(action, ".") { // instruction
					ip = action[0 : len(action)-1]
				} else { // also instruction
					ip = action
				}
				op, ok := opCode(ip)
				if !ok {
					panic("error op: " + ip)
				}
				params := processParameter(current, action, dataLine, false)
				params.name = op.String()
				l := &Label{is: true, n: getLineName(op.String(), line.Y), t: core.LABEL_OP, attrs: make(map[string]string), p: params}
				current.Top().Put(l)
				if current.IsDebug() {
					fmt.Println("> op", l.n)
				}
			}
		}
	}
}

func processPushLabel(current *Current, action string, dataLine string, line *Line) {
	params := processParameter(current, action, dataLine, true)
	l := &Label{is: true, n: getLineName(action, line.Y), t: core.LABEL_PUSH, attrs: make(map[string]string), p: params}
	current.Top().Put(l)
	if current.IsDebug() {
		fmt.Println("> push", l.n)
	}
}

func processValue(current *Current, valueString string, allowExpression bool) *Label {
	s, ok := getString(current, valueString)
	if ok {
		as := make(map[string]string)
		as[core.ATTRIBUTE_VALUE_NAME] = s
		return &Label{t: core.LABEL_VALUE, attrs: make(map[string]string), d: &Data{t: core.DATA_STRING, attrs: as}}
	}
	_, _, ok = isInt(valueString)
	if ok {
		as := make(map[string]string)
		as[core.ATTRIBUTE_VALUE_NAME] = valueString
		return &Label{t: core.LABEL_VALUE, attrs: make(map[string]string), d: &Data{t: core.DATA_INT, attrs: as}}
	}
	if allowExpression {
		ok = isExpression(valueString)
		if ok {
			as := make(map[string]string)
			as[core.ATTRIBUTE_VALUE_NAME] = valueString
			return &Label{t: core.LABEL_VALUE, attrs: make(map[string]string), d: &Data{t: core.DATA_INT, attrs: as}}
		}
	}
	l, ok := findData(current, valueString)
	if ok {
		return l
	}
	module := currentModule(current)
	l, ok = module.m.labelMap[valueString]
	if ok {
		return l
	}
	panic("error value: " + valueString)
}

func processCall(current *Current, callName string, callContent string) *Call {
	list := util.Split(callContent, ",")
	call := &Call{name: callName}
	for i := 0; i < len(list); i++ {
		item := strings.TrimSpace(list[i])
		value := processValue(current, item, false)
		call.params = append(call.params, value)
	}
	return call
}

func processParameter(current *Current, name string, content string, allowExpression bool) *Parameter {
	p := &Parameter{name: name}
	list := util.Split(content, ",")
	for i := 0; i < len(list); i++ {
		item := strings.TrimSpace(list[i])
		value := processValue(current, item, allowExpression)
		p.params = append(p.params, value)
	}
	return p
}

func processComment(current *Current, line *Line) {
	if current.IsDebug() {
		fmt.Println("comment", line.Y, "//", line.comment)
	}
	fields := strings.Fields(line.comment)
	ll := len(fields)
	if ll > 0 {
		action := fields[0]
		if strings.HasPrefix(action, "@") {
			current.commentMap[action] = append(current.commentMap[action], fields[1])
			if current.IsDebug() {
				fmt.Println("> comment", action, fields[1])
			}
		}
	}
}

func _findLabel(current *Current, b *Block, n string) (*Label, bool) {
	l, ok := b.nameMap[n]
	if ok {
		return l, true
	}
	for _, name := range b.nameList {
		l, ok := b.nameMap[name]
		if ok && l.b != nil {
			l, ok = _findLabel(current, l.b, n)
			if ok {
				return l, true
			}
		}
	}
	return nil, false
}

func findLabel(current *Current, n string) (*Label, bool) {
	root := current.Root()
	return _findLabel(current, root, n)
}

func findVisible(current *Current, n string) (*Label, bool) {
	p := current.Top()
	for p != nil {
		l, ok := p.nameMap[n]
		if ok {
			return l, true
		}
		if p.IsType(core.LABEL_FUNCTION) {
			for _, name := range p.nameList {
				l, ok := p.nameMap[name]
				if !ok {
					panic("error name map")
				}
				if l.b != nil && l.b.IsData() { // data block in function
					l, ok := l.b.nameMap[n]
					if ok {
						return l, true
					}
				}
			}
		}
		p = p.parent
	}
	return nil, false
}

func findData(current *Current, n string) (*Label, bool) {
	p := current.Top()
	for p != nil {
		if p.IsType(core.LABEL_FUNCTION) {
			l, ok := p.nameMap[n]
			if ok {
				return l, true
			}
			for _, name := range p.nameList {
				l, ok := p.nameMap[name]
				if !ok {
					panic("error name map")
				}
				if l.b != nil && l.b.IsData() { // data block in function
					l, ok := l.b.nameMap[n]
					if ok {
						return l, true
					}
				}
			}
		}
		p = p.parent
	}
	dataSection, ok := findLabel(current, SECTION_DATA)
	if ok { // data block in data section
		for _, name := range dataSection.b.nameList {
			l, ok := dataSection.b.nameMap[name]
			if !ok {
				panic("error name map")
			}
			if l.b != nil && l.b.IsData() {
				l, ok := l.b.nameMap[n]
				if ok {
					return l, true
				}
			}
		}
	}
	return nil, false
}

func processLabel(current *Current, line *Line) {
	if strings.HasSuffix(line.code, ":") { // label
		name := line.code[:len(line.code)-1]
		leftIndex := strings.Index(name, "(")
		if leftIndex == -1 {
			cm := currentModule(current)
			_, ok := cm.m.labelMap[name]
			if ok {
				panic("exists label: " + name)
			}
			l := &Label{n: name, t: core.LABEL_LABEL, attrs: make(map[string]string)}
			cm.m.labelMap[name] = l
		}
	}
}

func LoadModule(current *Current, modulePath string) {
	fileExt := path.Ext(modulePath)
	if fileExt != ASM_EXT {
		panic("error evm asm file")
	}
	fileName := path.Base(modulePath)
	moduleName := fileName[0 : len(fileName)-len(fileExt)]
	moduleName = util.GetName(moduleName)
	_, ok := findVisible(current, moduleName)
	if ok {
		panic("exists module: " + moduleName)
	}
	if current.Top().IsType(core.LABEL_MODULE) {
		current.Pop()
	}
	module := &Module{modulePath, make(map[string]*Label)}
	moduleLabel := &Label{n: moduleName, t: core.LABEL_MODULE, attrs: make(map[string]string), m: module}
	processPush(current, moduleLabel)

	content, err := os.ReadFile(modulePath)
	if err != nil {
		panic(err)
	}
	buffer := bytes.NewBuffer(content)
	br := bufio.NewReader(buffer)

	lines := make([]*Line, 0)
	y := 1 // line number in file
	for {
		lineBytes, isPrefix, err := br.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}
		if isPrefix {
			panic("line too long")
		}
		line := string(lineBytes)

		code := ""
		comment := ""
		list := util.Split(line, "//")
		switch len(list) {
		case 0:
			// empty line
		case 1:
			code = strings.TrimSpace(list[0])
		case 2:
			code = strings.TrimSpace(list[0])
			comment = strings.TrimSpace(list[1])
		default:
			panic("error line: " + line)
		}
		if len(comment) > 0 {
			processComment(current, &Line{X: 0, Y: y, code: code, comment: comment})
		}
		if len(code) > 0 {
			lines = append(lines, &Line{X: 0, Y: y, code: code})
		}
		y++
	}
	// process label
	for i := 0; i < len(lines); i++ {
		line := lines[i]
		processLabel(current, line)
	}
	for i := 0; i < len(lines); i++ {
		line := lines[i]
		processLine(current, line)
	}
}
